package my.suveng.util.log;


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.AbstractLog;
import cn.hutool.log.level.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;

public class PlusLogHutool extends AbstractLog {
    private static final long serialVersionUID = -6843151523380063975L;
    private final transient Logger logger;

    public PlusLogHutool(Logger logger) {
        this.logger = logger;
    }

    public PlusLogHutool(Class<?> clazz) {
        this(getSlf4jLogger(clazz));
    }

    public PlusLogHutool(String name) {
        this(LoggerFactory.getLogger(name));
    }

    private String getStrengthLog(String msgTemplate, Object[] arguments) {
        String log = StrUtil.format(msgTemplate, arguments);
        StringBuilder builder = new StringBuilder();

        LogDetail logDetail = LogDetail.get();
        if (ObjectUtil.isEmpty(logDetail)) {
            return log;
        }

        if (StrUtil.isBlank(logDetail.getTag())) {
            logDetail.setTag("noTag");
        }

        if (StrUtil.isBlank(logDetail.getRequsetId())) {
            logDetail.setRequsetId(IdUtil.fastSimpleUUID());
        }

        //tag标签
        builder.append("[").append(logDetail.getTag()).append("] ");

        //log原文
        builder.append("#").append(log);

        //requestid
        builder.append("#").append("requesetId=").append(logDetail.getRequsetId());

        return builder.toString();
    }

    @Override
    public String getName() {
        return this.logger.getName();
    }

    @Override
    public boolean isTraceEnabled() {
        return this.logger.isTraceEnabled();
    }

    @Override
    public void trace(String fqcn, Throwable t, String format, Object... arguments) {
        if (this.isTraceEnabled() && !this.locationAwareLog(fqcn, 0, t, format, arguments)) {
            this.logger.trace(StrUtil.format(format, arguments), t);
        }

    }

    @Override
    public boolean isDebugEnabled() {
        return this.logger.isDebugEnabled();
    }

    @Override
    public void debug(String fqcn, Throwable t, String format, Object... arguments) {
        if (this.isDebugEnabled() && !this.locationAwareLog(fqcn, 10, t, format, arguments)) {
            this.logger.debug(StrUtil.format(format, arguments), t);
        }

    }

    @Override
    public boolean isInfoEnabled() {
        return this.logger.isInfoEnabled();
    }

    @Override
    public void info(String fqcn, Throwable t, String format, Object... arguments) {
        if (this.isInfoEnabled() && !this.locationAwareLog(fqcn, 20, t, format, arguments)) {
            this.logger.info(StrUtil.format(format, arguments), t);
        }

    }

    @Override
    public boolean isWarnEnabled() {
        return this.logger.isWarnEnabled();
    }

    @Override
    public void warn(String fqcn, Throwable t, String format, Object... arguments) {
        if (this.isWarnEnabled() && !this.locationAwareLog(fqcn, 30, t, format, arguments)) {
            this.logger.warn(StrUtil.format(format, arguments), t);
        }

    }

    @Override
    public boolean isErrorEnabled() {
        return this.logger.isErrorEnabled();
    }

    @Override
    public void error(String fqcn, Throwable t, String format, Object... arguments) {
        if (this.isErrorEnabled() && !this.locationAwareLog(fqcn, 40, t, format, arguments)) {
            this.logger.error(StrUtil.format(format, arguments), t);
        }

    }

    @Override
    public void log(String fqcn, Level level, Throwable t, String format, Object... arguments) {
        byte level_int;
        switch (level) {
            case TRACE:
                level_int = 0;
                break;
            case DEBUG:
                level_int = 10;
                break;
            case INFO:
                level_int = 20;
                break;
            case WARN:
                level_int = 30;
                break;
            case ERROR:
                level_int = 40;
                break;
            default:
                throw new Error(StrUtil.format("Can not identify level: {}", level));
        }

        this.locationAwareLog(fqcn, level_int, t, format, arguments);
    }

    private boolean locationAwareLog(String fqcn, int levelInt, Throwable t, String msgTemplate, Object[] arguments) {
        if (this.logger instanceof LocationAwareLogger) {
            String log = this.getStrengthLog(msgTemplate, arguments);
            ((LocationAwareLogger) this.logger).log(null, fqcn, levelInt, log, null, t);
            return true;
        } else {
            return false;
        }
    }

    private static Logger getSlf4jLogger(Class<?> clazz) {
        return null == clazz ? LoggerFactory.getLogger("") : LoggerFactory.getLogger(clazz);
    }


}
